package cn.jbooter.repository.autoconfigure.transactional;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.Stack;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttribute;
import org.springframework.transaction.interceptor.TransactionInterceptor;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

/**
 * 支持多数据源事物的acid和spring的事务传播行为等操作
 * 
 * @author HeJian
 *
 */
public class MultiJDBCTransactionInterceptor extends TransactionInterceptor {
	private static final long serialVersionUID = -7172962847753805040L;

	@Autowired
	private ApplicationContext applicationContext;

	private String methodIdentification(Method method, Class<?> targetClass, TransactionAttribute txAttr) {
		String methodIdentification = methodIdentification(method, targetClass);
		if (methodIdentification == null) {
			if (txAttr instanceof DefaultTransactionAttribute) {
				methodIdentification = ((DefaultTransactionAttribute) txAttr).getDescriptor();
			}
			if (methodIdentification == null) {
				methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
			}
		}
		return methodIdentification;
	}

	public void batchCleanupTransactionInfo(Stack<TransactionInfo> txInfoStack) {
		for (int i = txInfoStack.size() - 1; i >= 0; i--) {
			TransactionInfo peek = txInfoStack.get(i);
			cleanupTransactionInfo(peek);
		}
	}

	public void batchCommitTransactionAfterReturning(Stack<TransactionInfo> txInfoStack) {
		while (!txInfoStack.isEmpty()) {
			TransactionInfo peek = txInfoStack.pop();
			commitTransactionAfterReturning(peek);
		}
	}

	public void batchCompleteTransactionAfterThrowing(Stack<TransactionInfo> txInfoStack, Throwable ex) {
		while (!txInfoStack.isEmpty()) {
			TransactionInfo peek = txInfoStack.pop();
			completeTransactionAfterThrowing(peek, ex);
		}
	}

	/**
	 * 获取spring容器中的事务管理器
	 * 
	 * 之所以用Stack来保存TransactionManager和TransactionStatus 是因为
	 * Spring的事务处理是按照LIFO/stack behavior的方式进行的。如若顺序有误，则会报错:
	 * 		java.lang.IllegalStateException: Cannot deactivate transaction synchronization - not active
	 * @param tmlStack
	 * @param txInfoStack
	 * @param txAttr
	 * @param joinpointIdentification
	 */
	public void createTransactionManager(Stack<PlatformTransactionManager> tmlStack, Stack<TransactionInfo> txInfoStack,
			TransactionAttribute txAttr, String joinpointIdentification) {

		if (txAttr == null || this.applicationContext == null) {
			PlatformTransactionManager tm = getTransactionManager();
			tmlStack.push(tm);
			TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
			txInfoStack.push(txInfo);
			return;
		}
		String qualifier = txAttr.getQualifier();
		if (StringUtils.hasText(qualifier)) {
			PlatformTransactionManager tm = BeanFactoryAnnotationUtils.qualifiedBeanOfType(applicationContext,
					PlatformTransactionManager.class, qualifier);
			tmlStack.push(tm);
			TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
			txInfoStack.push(txInfo);
			return;

		} else if (StringUtils.hasText(super.getTransactionManagerBeanName())) {
			PlatformTransactionManager tm = BeanFactoryAnnotationUtils.qualifiedBeanOfType(applicationContext,
					PlatformTransactionManager.class, super.getTransactionManagerBeanName());
			tmlStack.push(tm);
			TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
			txInfoStack.push(txInfo);
			return;

		} else {
			PlatformTransactionManager defaultTransactionManager = getTransactionManager();
			if (defaultTransactionManager == null) {
				Map<String, PlatformTransactionManager> tmMap = applicationContext.getBeansOfType(PlatformTransactionManager.class);
				if (tmMap != null && !tmMap.isEmpty()) {
					// 获取多个事物管理器
					for (Map.Entry<String, PlatformTransactionManager> tmEntry : tmMap.entrySet()) {
						PlatformTransactionManager tm = tmEntry.getValue();
						tmlStack.push(tm);
						TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
						txInfoStack.push(txInfo);
					}
				}
			}
		}
	}

	@Override
	protected Object invokeWithinTransaction(Method method, Class<?> targetClass, InvocationCallback invocation)
			throws Throwable {
		TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
		String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

		Stack<PlatformTransactionManager> tmlStack = new Stack<PlatformTransactionManager>();
		Stack<TransactionInfo> txInfoStack = new Stack<TransactionInfo>();
		createTransactionManager(tmlStack, txInfoStack, txAttr, joinpointIdentification);

		Object retVal = null;
		try {
			// This is an around advice: Invoke the next interceptor in the chain.
			// This will normally result in a target object being invoked.
			retVal = invocation.proceedWithInvocation();
		} catch (Throwable ex) {
			// target invocation exception
			batchCompleteTransactionAfterThrowing(txInfoStack, ex);
			throw ex;
		} finally {
			batchCleanupTransactionInfo(txInfoStack);
		}
		batchCommitTransactionAfterReturning(txInfoStack);
		return retVal;
	}

}
